/*
#############################################################################

  CommonFuncs.cpp - a library of common windows functions.

#############################################################################
*/

#include <string>
#include <d3dx9.h>
#include <windows.h>
#include <math.h>
#include <io.h>
#include <fcntl.h>
#include "Funcs.h"
#include "global.h"
#include <ClientResourceManager.h>
/****************************************************************************

 CenterWindow: centers the given window on the screen.

 ****************************************************************************/
void CenterWindow(HWND hwnd)
{
  // get the width and height of the screen
  int iScreenWidth = GetSystemMetrics(SM_CXFULLSCREEN);
  int iScreenHeight= GetSystemMetrics(SM_CYFULLSCREEN);

  // get the rectangle of the window
  RECT rectWindow;
  GetWindowRect(hwnd, &rectWindow);

  // do the centering math
  int iDestX = (iScreenWidth-(rectWindow.right-rectWindow.left)) / 2;
  int iDestY = (iScreenHeight-(rectWindow.bottom-rectWindow.top)) / 2;

  // center the window
  MoveWindow(hwnd, iDestX, iDestY, 
    rectWindow.right-rectWindow.left,
    rectWindow.bottom-rectWindow.top,
    true);
}

/****************************************************************************

 LoadFileIntoString: loads a file into a STL string.

 ****************************************************************************/
bool LoadFileIntoString(const char *strFilename, std::string &str)
{
	int handle = open(strFilename, O_RDONLY | O_BINARY);
	if (handle == -1) return(false);

	int iFilesize = filelength(handle);
	str.reserve(iFilesize);

	int iPos = 0; str = ""; bool bSuccess = true;
	while (iPos < iFilesize) {
		char buf[1024];
		memset(buf, 0, sizeof(buf));
		int iBytesRead = read(handle, buf, sizeof(buf)-1);
		str += buf;
		iPos += iBytesRead;
		if (iBytesRead == 0) { bSuccess = false; break; }
	}
	close(handle);
	return(bSuccess);
}

/****************************************************************************

 ConvertLastErrorToString: calls GetLastError, then converts it to a string
 using FormatMessage.  Puts up to nMaxStrLen characters of the converted
 error string into szDest.

 ****************************************************************************/
void ConvertLastErrorToString(LPSTR szDest, int nMaxStrLen)
{
  LPVOID lpMsgBuf;
  FormatMessage( 
    FORMAT_MESSAGE_ALLOCATE_BUFFER | 
    FORMAT_MESSAGE_FROM_SYSTEM | 
    FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL,
    GetLastError(),
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    (LPTSTR) &lpMsgBuf,
    0,
    NULL 
  );
  
  strncpy(szDest, reinterpret_cast<char *>(lpMsgBuf), nMaxStrLen);

  LocalFree( lpMsgBuf );
}

bool IsPowerOf2(int n) { return (!(n & (n - 1))); }

int GetLowestPowerOf2(int n)
{
  int lowest = 1;
  while(lowest < n) lowest <<= 1;
  return lowest;
}

/****************************************************************************

 RandomNumber: returns a random number between iMin and iMax.

 ****************************************************************************/
int RandomNumber(int iMin, int iMax)
{
  if (iMin == iMax) return(iMin);
  return((rand() % (abs(iMax-iMin)+1))+iMin);
}

float RandomNumber(float fMin, float fMax)
{
  if (fMin == fMax) return(fMin);
  float fRandom = (float)rand() / (float)RAND_MAX;
  return((fRandom * (float)fabs(fMax-fMin))+fMin);
}

/****************************************************************************
 *                                                                          *
 * PluckFirstField: pulls the first "field" off of str and puts it in dest  *
 * (uses delim as a delimiter)                                              *
 *                                                                          *
 ****************************************************************************/
void PluckFirstField(char *str, char *dest, int maxlen, 
                     const char *delim)
{
  char *endpos;
  int p;

  if (!strlen(delim)) { strcpy(dest, str); return; }
  endpos = strstr(str, delim);
  if (!endpos) { strcpy(dest, str); return; }
  p = endpos - str;

  memset(dest, 0, maxlen);
  memcpy(dest, str, p);

  // pluck it off of str...
  strcpy(str, &str[p+strlen(delim)]);
}


char *SurfFormatToString(D3DFORMAT srcformat)
{
  switch(srcformat) {
    case D3DFMT_UNKNOWN:      return("D3DFMT_UNKNOWN");      
                             
    case D3DFMT_R8G8B8:       return("D3DFMT_R8G8B8");       
    case D3DFMT_A8R8G8B8:     return("D3DFMT_A8R8G8B8");     
    case D3DFMT_X8R8G8B8:     return("D3DFMT_X8R8G8B8");     
    case D3DFMT_R5G6B5:       return("D3DFMT_R5G6B5");       
    case D3DFMT_X1R5G5B5:     return("D3DFMT_X1R5G5B5");     
    case D3DFMT_A1R5G5B5:     return("D3DFMT_A1R5G5B5");     
    case D3DFMT_A4R4G4B4:     return("D3DFMT_A4R4G4B4");     
    case D3DFMT_R3G3B2:       return("D3DFMT_R3G3B2");       
    case D3DFMT_A8:           return("D3DFMT_A8");           
    case D3DFMT_A8R3G3B2:     return("D3DFMT_A8R3G3B2");     
    case D3DFMT_X4R4G4B4:     return("D3DFMT_X4R4G4B4");     
                             
    case D3DFMT_A8P8:         return("D3DFMT_A8P8");         
    case D3DFMT_P8:           return("D3DFMT_P8");           
                             
    case D3DFMT_L8:           return("D3DFMT_L8");           
    case D3DFMT_A8L8:         return("D3DFMT_A8L8");         
    case D3DFMT_A4L4:         return("D3DFMT_A4L4");         
                             
    case D3DFMT_V8U8:         return("D3DFMT_V8U8");         
    case D3DFMT_L6V5U5:       return("D3DFMT_L6V5U5");       
    case D3DFMT_X8L8V8U8:     return("D3DFMT_X8L8V8U8");     
    case D3DFMT_Q8W8V8U8:     return("D3DFMT_Q8W8V8U8");     
    case D3DFMT_V16U16:       return("D3DFMT_V16U16");       
//    case D3DFMT_W11V11U10:    return("D3DFMT_W11V11U10");    
                                                                 
    case D3DFMT_UYVY:         return("D3DFMT_UYVY");         
    case D3DFMT_YUY2:         return("D3DFMT_YUY2");         
    case D3DFMT_DXT1:         return("D3DFMT_DXT1");         
    case D3DFMT_DXT2:         return("D3DFMT_DXT2");         
    case D3DFMT_DXT3:         return("D3DFMT_DXT3");         
    case D3DFMT_DXT4:         return("D3DFMT_DXT4");         
    case D3DFMT_DXT5:         return("D3DFMT_DXT5");         

    case D3DFMT_D16_LOCKABLE: return("D3DFMT_D16_LOCKABLE"); 
    case D3DFMT_D32:          return("D3DFMT_D32");          
    case D3DFMT_D15S1:        return("D3DFMT_D15S1");        
    case D3DFMT_D24S8:        return("D3DFMT_D24S8");        
    case D3DFMT_D16:          return("D3DFMT_D16");          
    case D3DFMT_D24X8:        return("D3DFMT_D24X8");        
    case D3DFMT_D24X4S4:      return("D3DFMT_D24X4S4");      

    case D3DFMT_VERTEXDATA:   return("D3DFMT_VERTEXDATA"); 
    case D3DFMT_INDEX16:      return("D3DFMT_INDEX16"); 
    case D3DFMT_INDEX32:      return("D3DFMT_INDEX32"); 
    default:                  return("**UNKNOWN**"); 
  };
  return("");
}

HRESULT CreateQuad(VertexBufferItem* *pVB, D3DPOOL pool, float fSize, 
                   DWORD dwColor, LPDIRECT3DDEVICE9 pDev,
                   float fTexTileX, float fTexTileY)
{
  HRESULT hr;

	*pVB=ResourcePoolManager->TerrainVertexBufferPool().createResource("pVB");
	u16Flags flag;
	if (pool==D3DPOOL_DEFAULT)flag=0;
		else flag=FLAG(VertexBufferItem::nSoftwareBit);
	if ((*pVB)->create(6,sizeof(VERTEX_XYZ_DIFFUSE_TEX1),flag,D3DFVF_XYZ_DIFFUSE_TEX1,NULL))return -1;
/*  if(FAILED(hr = pDev->CreateVertexBuffer(6*sizeof(VERTEX_XYZ_DIFFUSE_TEX1),
                  0, D3DFVF_XYZ_DIFFUSE_TEX1, pool, pVB,NULL)))
    return hr;*/

  VERTEX_XYZ_DIFFUSE_TEX1 *pVertices;
  float fSizeDiv2 = fSize/2;

  pVertices=(VERTEX_XYZ_DIFFUSE_TEX1 *)(*pVB)->lock( nWriteLock, 0,0);

  // first triangle
  pVertices[0].position = D3DXVECTOR3(-fSizeDiv2, fSizeDiv2, 0.0f);
  pVertices[0].color    = dwColor;
  pVertices[0].tu       = 0.0f;
  pVertices[0].tv       = 0.0f;

  pVertices[1].position = D3DXVECTOR3(fSizeDiv2, fSizeDiv2, 0.0f);
  pVertices[1].color    = dwColor;
  pVertices[1].tu       = fTexTileX;
  pVertices[1].tv       = 0.0f;

  pVertices[2].position = D3DXVECTOR3(fSizeDiv2, -fSizeDiv2, 0.0f);
  pVertices[2].color    = dwColor;
  pVertices[2].tu       = fTexTileX;
  pVertices[2].tv       = fTexTileY;

  // second triangle
  pVertices[3].position = D3DXVECTOR3(-fSizeDiv2, fSizeDiv2, 0.0f);
  pVertices[3].color    = dwColor;
  pVertices[3].tu       = 0.0f;
  pVertices[3].tv       = 0.0f;

  pVertices[4].position = D3DXVECTOR3(fSizeDiv2, -fSizeDiv2, 0.0f);
  pVertices[4].color    = dwColor;
  pVertices[4].tu       = fTexTileX;
  pVertices[4].tv       = fTexTileY;

  pVertices[5].position = D3DXVECTOR3(-fSizeDiv2, -fSizeDiv2, 0.0f);
  pVertices[5].color    = dwColor;
  pVertices[5].tu       = 0.0f;
  pVertices[5].tv       = fTexTileY;

  (*pVB)->unlock();
  return(S_OK);
}

HRESULT CreateVertexGrid(VertexBufferItem* *pVB, IndexBufferItem* *pIB,
                         D3DPOOL pool, float fTotalSize, DWORD dwColor, 
                         int iNumVerticesX, int iNumVerticesY, LPDIRECT3DDEVICE9 pDev)
{
  HRESULT hr;

  // create and fill vertex buffer
  {
	u16Flags flag;
	if (pool==D3DPOOL_DEFAULT)flag=0;
		else flag=FLAG(VertexBufferItem::nSoftwareBit);
		*pVB=ResourcePoolManager->TerrainVertexBufferPool().createResource("pVB");
		if (!(*pVB)->create(iNumVerticesX*iNumVerticesY,sizeof(VERTEX_XYZ_DIFFUSE_TEX1),flag,D3DFVF_XYZ_DIFFUSE_TEX1,NULL))return -1;
/*    if(FAILED(hr = pDev->CreateVertexBuffer(iNumVerticesX*iNumVerticesY*sizeof(VERTEX_XYZ_DIFFUSE_TEX1),
                    0, D3DFVF_XYZ_DIFFUSE_TEX1, pool, pVB,NULL)))
      return hr;*/

    VERTEX_XYZ_DIFFUSE_TEX1 *pVertices;
    float fSizeDiv2 = fTotalSize/2;

    pVertices=(VERTEX_XYZ_DIFFUSE_TEX1*)(*pVB)->lock( nWriteLock,0,0);

    for (int x=0; x < iNumVerticesX; x++) {
      for (int y=0; y < iNumVerticesY; y++) {
        pVertices[(y*iNumVerticesX)+x].position = D3DXVECTOR3(
          (iNumVerticesX > 1) ? (((float)x/(float)(iNumVerticesX-1))*fTotalSize)-fSizeDiv2 : 0, 
          (iNumVerticesY > 1) ? (((float)(iNumVerticesY-1-y)/(float)(iNumVerticesY-1))*fTotalSize)-fSizeDiv2 : 0, 
          0.0f);

        pVertices[(y*iNumVerticesX)+x].color    = dwColor;
        pVertices[(y*iNumVerticesX)+x].tu       = (float)x/(float)(iNumVerticesX-1);
        pVertices[(y*iNumVerticesX)+x].tv       = (float)y/(float)(iNumVerticesY-1);
      }
    }
  }

  // create index buffer
  {
		*pIB=ResourcePoolManager->TerrainIndexBufferPool().createResource("ClothIB");
		if (!(*pIB)->create(D3DPT_TRIANGLELIST,iNumVerticesX*iNumVerticesY*2*3,0,NULL))return -1;
/*    if(FAILED(hr = pDev->CreateIndexBuffer(
        iNumVerticesX*iNumVerticesY*2*3*2, // *2 (tris) *3 (indicies per tri) * 2 (bytes per index)
        D3DUSAGE_WRITEONLY, D3DFMT_INDEX16,
        g_Draw.DEVICE_VBPOOL, pIB,NULL)))
      return hr;*/

    // lock and fill index buffer

    WORD *pIndices;
    pIndices=(WORD *)(*pIB)->lock(nWriteLock,0,0);
  
    WORD *pIndex = pIndices;
    for (int x=0; x < iNumVerticesX-1; x++) {
      for (int y=0; y < iNumVerticesY-1; y++) {
        // first triangle
        *(pIndex++) = ((y)*iNumVerticesX)+x;
        *(pIndex++) = ((y)*iNumVerticesX)+x+1;
        *(pIndex++) = ((y+1)*iNumVerticesX)+x+1;

        // second triangle
        *(pIndex++) = ((y)*iNumVerticesX)+x;
        *(pIndex++) = ((y+1)*iNumVerticesX)+x+1;
        *(pIndex++) = ((y+1)*iNumVerticesX)+x;
      }
    }

    (*pIB)->unlock();
    (*pVB)->unlock();
  }
  return(S_OK);

}

HRESULT CreateVertexFrame(VertexBufferItem* *pVB, IndexBufferItem* *pIB,
                         D3DPOOL pool, float fTotalSize, DWORD dwColor, 
                         int iNumVerticesX, int iNumVerticesY, LPDIRECT3DDEVICE9 pDev)
{
  HRESULT hr;

  // create and fill vertex buffer
  {
	u16Flags flag;
	if (pool==D3DPOOL_DEFAULT)flag=0;
		else flag=FLAG(VertexBufferItem::nSoftwareBit);
		*pVB=ResourcePoolManager->TerrainVertexBufferPool().createResource("pVB");
		if (!(*pVB)->create(((iNumVerticesX-1)*2)+((iNumVerticesY-1)*2)+2,sizeof(VERTEX_XYZ_DIFFUSE_TEX1),flag,D3DFVF_XYZ_DIFFUSE_TEX1,NULL))return -1;
/*    if(FAILED(hr = pDev->CreateVertexBuffer((((iNumVerticesX-1)*2)+((iNumVerticesY-1)*2)+2)*sizeof(VERTEX_XYZ_DIFFUSE_TEX1),
      0, D3DFVF_XYZ_DIFFUSE_TEX1, pool, pVB,NULL)))
      return hr;*/

    VERTEX_XYZ_DIFFUSE_TEX1 *pVertices;
    float fSizeDiv2 = fTotalSize/2;

    pVertices=(VERTEX_XYZ_DIFFUSE_TEX1*)(*pVB)->lock( nWriteLock,0,0);
    
    // top row
    for (int x=0; x < iNumVerticesX; x++) {
      pVertices->position = D3DXVECTOR3(
        (((float)x/(float)(iNumVerticesX-1))*fTotalSize)-fSizeDiv2, 
        fSizeDiv2, 
        0.0f);

      pVertices->color    = dwColor;
      pVertices->tu       = (float)x/(float)(iNumVerticesX-1);
      pVertices->tv       = 0.0f;
      pVertices++;
    }
    
    // right row
    for (int y=1; y < iNumVerticesY; y++) {
      pVertices->position = D3DXVECTOR3(
        fSizeDiv2, 
        (((float)(iNumVerticesY-1-y)/(float)(iNumVerticesY-1))*fTotalSize)-fSizeDiv2, 
        0.0f);

      pVertices->color    = dwColor;
      pVertices->tu       = 1.0f;
      pVertices->tv       = (float)y/(float)(iNumVerticesY-1);
      pVertices++;
    }

    // bottom row
    for (int x=iNumVerticesX-1; x >= 1; x--) {
      pVertices->position = D3DXVECTOR3(
        (((float)x/(float)(iNumVerticesX-1))*fTotalSize)-fSizeDiv2, 
        -fSizeDiv2, 
        0.0f);

      pVertices->color    = dwColor;
      pVertices->tu       = (float)x/(float)(iNumVerticesX-1);
      pVertices->tv       = 1.0f;
      pVertices++;
    }
    
    // left row
    for (int y=iNumVerticesY-1; y >= 1; y--) {
      pVertices->position = D3DXVECTOR3(
        -fSizeDiv2, 
        (((float)(iNumVerticesY-1-y)/(float)(iNumVerticesY-1))*fTotalSize)-fSizeDiv2, 
        0.0f);

      pVertices->color    = dwColor;
      pVertices->tu       = 0.0f;
      pVertices->tv       = (float)y/(float)(iNumVerticesY-1);
      pVertices++;
    }

    
    // last point in exact center
    pVertices->position = D3DXVECTOR3(0.0f,0.0f, 0.0f);
    pVertices->color    = dwColor;
    pVertices->tu       = 0.5f;
    pVertices->tv       = 0.5f;
    
  }

  // create index buffer
  {
		*pIB=ResourcePoolManager->TerrainIndexBufferPool().createResource("ClothIB");
		if (!(*pIB)->create(D3DPT_TRIANGLELIST,(((iNumVerticesX-1)*2)+((iNumVerticesY-1)*2))*3,0,NULL))return -1;
/*    if(FAILED(hr = pDev->CreateIndexBuffer(
        (((iNumVerticesX-1)*2)+((iNumVerticesY-1)*2))*3*2, // *3 (indicies per tri) * 2 (bytes per index)
        D3DUSAGE_WRITEONLY, D3DFMT_INDEX16,
        g_Draw.DEVICE_VBPOOL, pIB,NULL)))
      return hr;*/

    // lock and fill index buffer

    WORD *pIndices;
    pIndices=(WORD *)(*pIB)->lock(nWriteLock,0,0);
  
    // the triangles are arranged in a fan pattern... 
    // probably could have used tri fans here but GeForce class cards like tri lists better
    WORD *pIndex = pIndices;
    for (int q=0; q < (((iNumVerticesX-1)*2)+((iNumVerticesY-1)*2)); q++) {
      // first triangle
      *(pIndex++) = q;
      *(pIndex++) = ((iNumVerticesX-1)*2)+((iNumVerticesY-1)*2);
      *(pIndex++) = ((q+1 == ((iNumVerticesX-1)*2)+((iNumVerticesY-1)*2)) ? 0 : q+1);
    }

    (*pIB)->unlock();
    (*pVB)->unlock();
  }
  return(S_OK);

}


/*
HRESULT RenderCopy(LPDIRECT3DTEXTURE9 ptexSource, LPDIRECT3DTEXTURE9 ptexDest,
                   int iDestWidth, int iDestHeight, LPDIRECT3DDEVICE9 pDev,
                   VertexBufferItem* pvbQuad)*/
HRESULT RenderCopy(TextureItem* ptexSource, TextureItem* ptexDest,
                   int iDestWidth, int iDestHeight, LPDIRECT3DDEVICE9 pDev,
                   VertexBufferItem* pvbQuad)
{
  HRESULT hr;
  // get the current depth buffer (we have to pass this into SetRenderTarget
  // so we don't inadvertently drop our depth buffer.)
  LPDIRECT3DSURFACE9 pDepthSurf;
  pDev->GetDepthStencilSurface(&pDepthSurf);

  // get the current rendering target (we'll set it back later)
  LPDIRECT3DSURFACE9 pOldRenderTarget;
  pDev->GetRenderTarget(0,&pOldRenderTarget);

  // get surface interfaces
  LPDIRECT3DSURFACE9 psurfDest;
  if (!ptexDest)return -1;
  ptexDest->getTexture()->GetSurfaceLevel(0, &psurfDest);

  // set new rendering target & clear
  pDev->SetRenderTarget(0,psurfDest);
  pDev->Clear( 0L, NULL, D3DCLEAR_TARGET, 0x000000, 1.0f, 0L );
  
  // turn off z buffering
  pDev->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
  
  // set up texture stages for simple texture stage copy
	pDev->SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1);
	pDev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
	pDev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
	pDev->SetTextureStageState(1, D3DTSS_COLOROP,   D3DTOP_DISABLE); 
	pDev->SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);

  SetupOrthoProjForRenderCopy(pDev, iDestWidth, iDestHeight);

  // set the source texture active
  if (ptexSource)pDev->SetTexture(0, ptexSource->getTexture());

  // begin rendering the scene
  if (FAILED(hr = pDev->BeginScene())) return hr;
//  pDev->SetStreamSource( 0, pvbQuad, 0,sizeof(VERTEX_XYZ_DIFFUSE_TEX1));
  pvbQuad->activate(0,0,0);
  pDev->SetFVF( D3DFVF_XYZ_DIFFUSE_TEX1 );

  // this "blits" the texture
  pDev->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 2 );

  // end scene
  pDev->EndScene();
  
  // release the dest surface 
  psurfDest->Release();

  // set the rendering target back to the old target.
//  pDev->SetRenderTarget(pOldRenderTarget, pDepthSurf);
  pDev->SetRenderTarget(0,pOldRenderTarget);
  pDev->SetRenderState(D3DRS_ZENABLE, true);
  pOldRenderTarget->Release();

  // release the depth surface interface.
  pDepthSurf->Release();
  return S_OK;
}

/*
HRESULT SmoothRenderCopy(LPDIRECT3DTEXTURE9 ptexSource, LPDIRECT3DTEXTURE9 ptexDest, 
                         int iDestWidth, int iDestHeight, LPDIRECT3DDEVICE9 pDev, 
                         VertexBufferItem* pvbQuad)
						 */
HRESULT SmoothRenderCopy(TextureItem* ptexSource, TextureItem* ptexDest, 
                         int iDestWidth, int iDestHeight, LPDIRECT3DDEVICE9 pDev, 
                         VertexBufferItem* pvbQuad)
{
  // no alpha blending or lighting
  pDev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
	pDev->SetRenderState(D3DRS_LIGHTING, FALSE);

  // turn on bilinear filtering (this is what "smooths the noise")
  pDev->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
  pDev->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
  pDev->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
  
  // render-copy the texture
  return(RenderCopy(ptexSource, ptexDest, iDestWidth, iDestHeight, pDev, pvbQuad));
}

HRESULT SetupOrthoProjForRenderCopy(LPDIRECT3DDEVICE9 pDev, int iWidth, int iHeight)
{
  // set up orthogonal projection matrix, so that one texel = one pixel in the final
  // image, with no perspective correction.
  D3DXMATRIX projmat;
  D3DXMatrixOrthoLH(&projmat, (float)iWidth, (float)iHeight, 0.0, 100.0);
  pDev->SetTransform( D3DTS_PROJECTION, &projmat );

  // set up world matrix so that it rotates the quad slightly.
  D3DXMATRIX worldmattemp, worldmat, transmat;
  D3DXMatrixScaling(&worldmattemp, (float)iWidth/2.0f, (float)iHeight/2.0f, 1.0);

  // move the quad left and up 0.5 units, so that the texels are perfectly
  // centered on the screen pixels.
  D3DXMatrixMultiply(&worldmat, &worldmattemp, D3DXMatrixTranslation(&transmat, -0.5f, -0.5f, 0));

  pDev->SetTransform( D3DTS_WORLD, &worldmat );

  // set up view matrix
  D3DXMATRIX viewmat;
  D3DXMatrixIdentity(&viewmat);
  pDev->SetTransform( D3DTS_VIEW, &viewmat );
  return S_OK;
}

//-----------------------------------------------------------------------------
// Name: DoesRayIntersectTriangle()
// Desc: Given a ray origin (orig) and direction (dir), and three vertices of
//       of a triangle, this function returns TRUE and the interpolated texture
//       coordinates if the ray intersects the triangle
//-----------------------------------------------------------------------------
bool DoesRayIntersectTriangle( const D3DXVECTOR3& orig,
                               const D3DXVECTOR3& dir, D3DXVECTOR3& v0,
                               D3DXVECTOR3& v1, D3DXVECTOR3& v2,
                               FLOAT* t, FLOAT* u, FLOAT* v )
{
    // Find vectors for two edges sharing vert0
    D3DXVECTOR3 edge1 = v1 - v0;
    D3DXVECTOR3 edge2 = v2 - v0;

    // Begin calculating determinant - also used to calculate U parameter
    D3DXVECTOR3 pvec;
    D3DXVec3Cross( &pvec, &dir, &edge2 );

    // If determinant is near zero, ray lies in plane of triangle
    FLOAT det = D3DXVec3Dot( &edge1, &pvec );
    if( det < 0.0001f )
        return false;

    // Calculate distance from vert0 to ray origin
    D3DXVECTOR3 tvec = orig - v0;

    // Calculate U parameter and test bounds
    *u = D3DXVec3Dot( &tvec, &pvec );
    if( *u < 0.0f || *u > det )
        return false;

    // Prepare to test V parameter
    D3DXVECTOR3 qvec;
    D3DXVec3Cross( &qvec, &tvec, &edge1 );

    // Calculate V parameter and test bounds
    *v = D3DXVec3Dot( &dir, &qvec );
    if( *v < 0.0f || *u + *v > det )
        return false;

    // Calculate t, scale parameters, ray intersects triangle
    *t = D3DXVec3Dot( &edge2, &qvec );
    FLOAT fInvDet = 1.0f / det;
    *t *= fInvDet;
    *u *= fInvDet;
    *v *= fInvDet;

    return true;
}
/*
HRESULT CreateShader(LPDIRECT3DDEVICE9 pDev, const char *strFilename, 
                     DWORD *pDecl, LPDIRECT3DVERTEXSHADER9 &dwID)
{
  HRESULT hr;
  LPD3DXBUFFER pCode;

  // Assemble the shader from the file
  if( FAILED( hr = D3DXAssembleShaderFromFile( strFilename, 
                                               0, NULL, NULL,&pCode, NULL ) ) )
    return hr;

  // if PDecl is not null, we know this is a vertex shader.
  if (pDecl) {
    hr = pDev->CreateVertexShader(  
      (DWORD*)pCode->GetBufferPointer(), dwID );
  }
  else {
    // it's a pixel shader!
    hr = pDev->CreatePixelShader((DWORD *)pCode->GetBufferPointer(), &dwID);
  }
  
  pCode->Release();
  if( FAILED(hr) ) return hr;
  return(S_OK);
}
*/